﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Reflection;
using System.Text;
using DomainDriver.DomainModeling.DataProviders;
using DomainDriver.DomainModeling.DomainObjects;
using DomainDriver.DomainModeling.StorageManagers.EntityFrameworkStorage;

namespace DomainDriver.DomainModeling.Queries
{
    public class ContainsQuery<T> : IParameterizedQuery<T>
    {
        private IDataProvider m_DataProvider;
        private string m_ParameterName;
        private object m_DesiredParameterValue;

        public ContainsQuery(string parameterName, object desiredParameterValue)
        {
            if (parameterName == null)
            { throw new ApplicationException(); }

            m_ParameterName = parameterName;
            m_DesiredParameterValue = desiredParameterValue;
        }

        public IList<string> Parameters
        {
            get
            {
                List<string> parameters = new List<string>();
                parameters.Add(m_ParameterName);
                return parameters;
            }
        }

        public IDictionary<string, object> ParameterValues
        {
            get
            {
                Dictionary<string, object> parameterValues = new Dictionary<string, object>();
                parameterValues.Add(m_ParameterName, m_DesiredParameterValue);
                return parameterValues;
            }
        }

        public void SetQueryParameter(string parameterName, object value)
        {
            if (!m_ParameterName.Equals(parameterName))
            { throw new ApplicationException(); }

            m_DesiredParameterValue = value;
        }

        public Func<T, bool> GetQueryPredicate(IDataProvider dataProvider)
        {
            m_DataProvider = dataProvider;
            return CheckContainsQuery;
        }

        protected bool CheckContainsQuery(T objectToCheck)
        {
            if ((objectToCheck is IEfAggregate<T>) && (m_DataProvider.PersistenceType == DataSourcePersistenceType.Database))
            {
                QuerySpecification specification = new QuerySpecification(this.GetType(), Parameters.ToArray<string>());
                IEfAggregate<T> aggregateRoot = objectToCheck as IEfAggregate<T>;

                if (aggregateRoot.OverriddenQueries.Contains(specification))
                {
                    return ((IEfAggregate<T>)objectToCheck).SearchNestedAggregateValues(m_DataProvider.DataSource as DbContext, this);
                }
            }

            Type objectType = typeof(T);
            PropertyInfo propertyInfo = objectType.GetProperty(m_ParameterName);

            if (propertyInfo == null)
            { throw new ApplicationException("The property name in the query is invalid."); }

            object valueFromQuery = m_DesiredParameterValue;
            object valueToCheck = propertyInfo.GetValue(objectToCheck, null);

            if ((valueFromQuery == null) || (valueToCheck == null))
            { return false; }
            if (!(valueToCheck is IEnumerable))
            { return false; }

            Type typeOfValueFromQuery = valueFromQuery.GetType();
            IEnumerable collection = valueToCheck as IEnumerable;
            foreach (object containedValue in collection)
            {
                Type typeOfContainedValue = containedValue.GetType();

                if (typeOfContainedValue.Equals(typeOfValueFromQuery))
                {
                    if (containedValue.Equals(valueFromQuery))
                    { return true; }
                }
            }
            return false;
        }
    }
}